/******************************************************************************************************
Copyright (C) 2010 Mestrelab Research S.L. All rights reserved.

This file is part of the MNova scripting toolkit.

Authorized users of MNova Software may use this file freely, but this file is provided AS IS
with NO WARRANTY OF ANY KIND, INCLUDING THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE.
*****************************************************************************************************/

/*globals draw, Env, FontDialog, mainWindow, MessageBox, mnDebug, Molecule, NMRAssignments, NMRSpectrum, Peak, Peaks, PeakReportDialog, PeakTable, settings, Str*/
/*jslint plusplus: true, indent: 4, bitwise: true*/

// This function defines peak list report.
// To customize report, edit this function or implement another one.
function PeakReporter() {
	'use strict';

	this.name = 'Custom';
	this.alias = undefined;
	// fields to be filled in
	this.font = '';
	this.header = '';
	this.nucleusTemplate = '';
	this.onlyElementName = false;
	this.peakTemplate = '';
	this.peakTemplateWith13CMult = '%1 (%C13MULT%)';
	this.peakTemplateWithAssignments = '%1 (%ASSIGNMENT%)';
	this.assignmentsTemplate = "";
	this.deltaPrecision = 2;
	this.pSeparator = '';
	this.start = '';
	this.end = '';
	this.sortHAscending = false;
	this.sortJAscending = false;
	this.compoundOnly = true;
	this.assignedMolecules = undefined;
	this.report13CAssignments = false;
	this.report13CMultiplicity = false;
	this.fFlags = PeakReporter.ReportDefaultFlags;
	this.extendedSolventName = false;
	this.fillStyle = 'Transparent';
	this.reportType = 'Peak Report';
	this.reporterName = 'J. Am. Chem. Soc.';
	this.fontProperties = PeakReporter.getDefaultFont();
	this.fontColor = '#000000';
}

PeakReporter.templateSettings = "Peak Report/Template";
PeakReporter.sortHAscendingSettings = "Peak Report/Sort H Ascending";
PeakReporter.sortJAscendingSettings = "Peak Report/Sort Js Ascending";
PeakReporter.compoundOnlySettings = "Peak Report/Compound Only";
PeakReporter.report13CAssignmentsSettings = "Peak Report/Report 13C Assignments";
PeakReporter.report13CMultiplicitySettings = "Peak Report/Report 13C Multiplicity";
PeakReporter.deltaPrecisionSettings = "Peak Report/Delta Precision";
PeakReporter.flagsSettings = "Peak Report/Flags";
PeakReporter.extendedSolventNameSettings = "Peak Report/Extended Solvent Name";
PeakReporter.fillStyleSettings = "Peak Report/Fill Style";
PeakReporter.fontPropertiesSettings = "Peak Report/Font Properties";
PeakReporter.fontColorSettings = "Peak Report/Font Color";
PeakReporter.ReportF1 = 0x1;
PeakReporter.ReportF2 = 0x2;
PeakReporter.ReportAsPoint = 0x4;
PeakReporter.ReportDefaultFlags = PeakReporter.ReportF1 | PeakReporter.ReportF2;//by default reports both dimension separatedly

PeakReporter.reportPeaksUser = function (createItem, props) {
	'use strict';

	var peakTable,
		spectrum,
		tableSpec,
		reporter,
		reporterSettings = PeakReporter.readSettings();

	if (props !== undefined) {
		spectrum = new NMRSpectrum(mainWindow.activeDocument.getItem(props.linkID));
		reporter = PeakReporter.getReporterByName(props.reporterName);
	}
	if (spectrum === undefined || !spectrum.isValid()) {
		if (mainWindow.hasGui) {
			peakTable = new PeakTable(mainWindow.getObject("NMRPeaksTable"));
			if (peakTable && peakTable.isValid()) {
				tableSpec = peakTable.spectrum();
				if (tableSpec !== undefined && tableSpec.isValid()) {
					spectrum = new NMRSpectrum(peakTable.spectrum()); // Get spectrum currently shown in the table of peaks
				}
			}
		}
	}	
	if (spectrum === undefined || !spectrum.isValid()) {
		spectrum = new NMRSpectrum(mainWindow.activeDocument.getActiveItem("NMR Spectrum")); // Get active spectrum
	}
	if (spectrum === undefined || !spectrum.isValid()) {
		return undefined;
	}

	if (reporter === undefined) {
		reporter = PeakReporter.getDefaultReporter();
	}

	if (reporter !== undefined) {
		reporter.copySettings(reporterSettings);
		if (props !== undefined) {
			reporter.fontProperties = JSON.parse(JSON.stringify(props.fontProperties));
			reporter.fontColor = props.fontColor;
			reporter.sortHAscending = props.sortHAscending;
			reporter.sortJAscending = props.sortJAscending;
			reporter.compoundOnly = props.compoundOnly;
			reporter.report13CAssignments = props.report13CAssignments;
			reporter.report13CMultiplicity = props.report13CMultiplicity;
			reporter.extendedSolventName = props.extendedSolventName;
			reporter.deltaPrecision = props.deltaPrecision;
			reporter.fillStyle = props.fillStyle;
			reporter.fFlags = props.fFlags;
		}

		return reporter.report(spectrum, createItem);
	}
}

PeakReporter.getReporterByName = function (aName) {
	'use strict';

	var i, rep;

	for (i = 0; i < Env.PeakReportersArray.length; ++i) {
		rep = Env.PeakReportersArray[i];
		if (rep.name === aName || (rep.alias && rep.alias.indexOf(aName) !== -1)) {
			return Env.PeakReportersArray[i];
		}
	}
	return undefined;
}

PeakReporter.getDefaultReporter = function (aDefValue) {
	'use strict';

	var defaultName, retVal;

	if (!aDefValue) {
		aDefValue = "J. Am. Chem. Soc.";
	}
	if (Env.DefaultPeakReporter !== undefined) {
		retVal = PeakReporter.getReporterByName(Env.DefaultPeakReporter);
		if (retVal !== undefined) {
			return retVal;
		}
		return PeakReporter.getReporterByName(aDefValue);
	}
	defaultName = settings.value(PeakReporter.templateSettings, aDefValue);
	retVal = PeakReporter.getReporterByName(defaultName);
	if (retVal !== undefined) {
		return retVal;
	}
	return PeakReporter.getReporterByName(aDefValue);
}

PeakReporter.getDefaultFont = function () {
	'use strict';

	var font = {};
	font.size = 10;
	font.family = "Times New Roman";

	return font;
}

PeakReporter.readSettings = function () {
	'use strict';

	return {
		reporterName : settings.value(PeakReporter.templateSettings, 'J. Am. Chem. Soc.'),
		sortHAscending : settings.value(PeakReporter.sortHAscendingSettings, false),
		sortJAscending : settings.value(PeakReporter.sortJAscendingSettings, false),
		compoundOnly : settings.value(PeakReporter.compoundOnlySettings, false),
		report13CAssignments : settings.value(PeakReporter.report13CAssignmentsSettings, false),
		deltaPrecision : settings.value(PeakReporter.deltaPrecisionSettings, 2),
		fFlags : settings.value(PeakReporter.flagsSettings, PeakReporter.ReportDefaultFlags),
		report13CMultiplicity: settings.value(PeakReporter.report13CMultiplicitySettings, false),
		extendedSolventName: settings.value(PeakReporter.extendedSolventNameSettings, false),
		fillStyle: settings.value(PeakReporter.fillStyleSettings, 'Transparent'),
		fontProperties : JSON.parse(settings.value(PeakReporter.fontPropertiesSettings, JSON.stringify(PeakReporter.getDefaultFont()))),
		fontColor: settings.value(PeakReporter.fontColorSettings, '#000000')
	};
}

PeakReporter.writeSettings = function (aReporter) {
	'use strict';

	settings.setValue(PeakReporter.templateSettings, aReporter.reporterName);
	settings.setValue(PeakReporter.sortHAscendingSettings, aReporter.sortHAscending);
	settings.setValue(PeakReporter.sortJAscendingSettings, aReporter.sortJAscending);
	settings.setValue(PeakReporter.compoundOnlySettings, aReporter.compoundOnly);
	settings.setValue(PeakReporter.report13CAssignmentsSettings, aReporter.report13CAssignments);
	settings.setValue(PeakReporter.deltaPrecisionSettings, aReporter.deltaPrecision);
	settings.setValue(PeakReporter.flagsSettings, aReporter.fFlags);
	settings.setValue(PeakReporter.report13CMultiplicitySettings, aReporter.report13CMultiplicity);
	settings.setValue(PeakReporter.extendedSolventNameSettings, aReporter.extendedSolventName);
	settings.setValue(PeakReporter.fillStyleSettings, aReporter.fillStyle);
	settings.setValue(PeakReporter.fontPropertiesSettings, JSON.stringify(aReporter.fontProperties));
	settings.setValue(PeakReporter.fontColorSettings, aReporter.fontColor);
}

PeakReporter.prototype = {
	toString : function () {
		'use strict';

		return "PeakReporter()";
	},

	copySettings : function (aSettings) {
		'use strict';

		var prop;

		for (prop in aSettings) {
			if (aSettings.hasOwnProperty(prop)) {
				if (prop === 'fontProperties') {
					this.fontProperties = JSON.parse(JSON.stringify(aSettings.fontProperties));
				}
				else {
					this[prop] = aSettings[prop];
				}
			}
		}
	},

	getProperties : function () {
		'use strict';

		return {
			reportType : this.reportType,
			reporterName : this.reporterName,
			sortHAscending : this.sortHAscending,
			sortJAscending : this.sortJAscending,
			compoundOnly : this.compoundOnly,
			report13CAssignments : this.report13CAssignments,
			deltaPrecision : this.deltaPrecision,
			fFlags : this.fFlags,
			report13CMultiplicity: this.report13CMultiplicity,
			extendedSolventName: this.extendedSolventName,
			fillStyle: this.fillStyle,
			fontProperties : JSON.parse(JSON.stringify(this.fontProperties)),
			fontColor: this.fontColor
		};
	},

	assignmentString : function (aDim, aPeak, aSpectrum) {
		'use strict';

		var nucleus,
			nmrAssignObject,
			tmpString,
			pageItem,
			mol,
			i,
			assignString;

		if (this.assignedMolecules === undefined) {
			this.assignedMolecules = aSpectrum.getNMRAssignedMolecules();
		}
		if (this.assignedMolecules.length === 0) {
			return "";
		}
		nucleus = aSpectrum.nucleus(aDim, false);
		assignString = "";
		nmrAssignObject = undefined;
		tmpString = "";
		for (i = 0; i < this.assignedMolecules.length; ++i) {
			pageItem = this.assignedMolecules[i];
			mol = new Molecule(pageItem);
			if (mol.isValid()) {
				nmrAssignObject = new NMRAssignments(mol);
				tmpString = nmrAssignObject.peakAssignment(aPeak.id, nucleus);
				if (tmpString.length !== 0) {
					assignString += tmpString + ", ";
				}
			}
		}
		assignString = assignString.replace(/,\s*$/, "");

		return assignString;
	},

	reportDimension : function (dim, peaks, spectrum) {
		'use strict';

		var pTemplate = this.peakTemplate,
			nucleusString = '',
			fontString = '',
			result = '',
			pCount = 0,
			addedPeaksCount = 0,
			isCarbon = (spectrum.nucleus(dim, false) === "13C"),
			i = 0,
			j = 0,
			peak,
			pString = '',
			assignStr = '',
			assignmentsTemplate,
			assignmentAtoms,
			newAssignmentsValue,
			atomNumber,
			numberRegExp,
			nucleusStrForAssignments;

		if (this.report13CAssignments) {
			pTemplate = this.peakTemplateWithAssignments;
		} else if (isCarbon && this.report13CMultiplicity) {
			pTemplate = this.peakTemplateWith13CMult;
		}

		if (this.onlyElementName) {
			nucleusString = Str.removeNumbers(spectrum.nucleus(dim));
		} else {
			nucleusString = spectrum.nucleus(dim, true);
		}
		nucleusString = this.nucleusTemplate.replace("%1", nucleusString);
		result = this.header.replace("%1", nucleusString).replace("%2", Math.round(spectrum.frequency(dim))).replace("%3", Str.solventStr2Html(spectrum.solventName(this.extendedSolventName)));

		pCount = peaks.count;
		addedPeaksCount = 0;

		//Get the nucleus letter for atom labeling
		if (spectrum.dimCount === 1) {
			nucleusStrForAssignments = spectrum.nucleus(false);
			if (nucleusStrForAssignments === "1H" || nucleusStrForAssignments === "13C") {
				numberRegExp = /[0-9]*/;
				nucleusStrForAssignments = nucleusStrForAssignments.replace(numberRegExp, "");
			} else {
				nucleusStrForAssignments = undefined;
			}
		}

		for (i = 0; i < pCount; i++) {
			peak = new Peak(peaks.at(i));
			if (spectrum.checkPeakVisibility(peak) && (!this.compoundOnly || peak.type === Peak.Compound)) { //only the peaks shown in the user interface will be reported
				if (addedPeaksCount !== 0) {
					result += this.pSeparator;
				}
				pString = '';
				pString = pTemplate.replace("%1", peak.delta(dim).toFixed(this.deltaPrecision));
				if (pString.indexOf("%ASSIGNMENT%") !== -1) {
					assignStr = "";
					if (isCarbon) {
						assignStr = this.assignmentString(dim, peak, spectrum);
						if (this.report13CMultiplicity) {
							if (assignStr) {
								assignStr = "%1, %2".arg(peak.c13Multiplicity).arg(assignStr);
							} else {
								assignStr = "%1".arg(peak.c13Multiplicity);
							}
						}
					}

					//Get each atom number and applies the assignments template for each atom
					assignmentAtoms = assignStr.split(", ");
					assignmentsTemplate = this.assignmentsTemplate;
					newAssignmentsValue = "";
					for (j = 0; j < assignmentAtoms.length; j++) {
						atomNumber = assignmentAtoms[j];

						if (atomNumber !== "") {
							//We only take into account atom numbers for 1H and 13C nucleus type
							if (nucleusStrForAssignments !== undefined && !isNaN(atomNumber)) {
								newAssignmentsValue += assignmentsTemplate.replace("%1", atomNumber).replace("%2", nucleusStrForAssignments);

								//Multiplicities and other kind of nucleus will be skipped
							} else {
								newAssignmentsValue += atomNumber;
							}

							//Add the token separator except for the last one
							if (j !== (assignmentAtoms.length - 1)) {
								newAssignmentsValue += ", ";
							}
						}
					}
					pString = pString.replace("%ASSIGNMENT%", newAssignmentsValue);
				} else if (isCarbon && pString.indexOf("%C13MULT%") !== -1) {
					pString = pString.replace("%C13MULT%", peak.c13Multiplicity || "");
				}

				result += pString;
				addedPeaksCount++;
			}
		}
		result = result.replace(/\s*\(\s*\)\s*/g, "");//removes empty ()
		result = result.replace(/,\s*,/g, ",");//removes , ,

		return result;
	},

	reportAsPoint : function (peaks, spectrum) {
		'use strict';

		var pTemplate = '(%2 %1)', //%2 f2, %1 f1
			nucleusString = '%2-%1',
			result = '',
			pCount = peaks.count,
			addedPeaksCount = 0,
			i = 0,
			peak,
			pString = '',
			frequencyString = '(%2, %1)';

		if (spectrum.dimCount === 1) {
			return this.reportDimension(1, peaks, spectrum);
		}

		if (this.onlyElementName) {
			nucleusString = nucleusString.arg(Str.removeNumbers(spectrum.nucleus(1))).arg(Str.removeNumbers(spectrum.nucleus(2)));
		} else {
			nucleusString = nucleusString.arg(spectrum.nucleus(1, true)).arg(spectrum.nucleus(2, true));
		}
		nucleusString = this.nucleusTemplate.replace("%1", nucleusString);
		frequencyString = frequencyString.arg(Math.round(spectrum.frequency(1))).arg(Math.round(spectrum.frequency(2)));
		result = this.header.replace("%1", nucleusString).replace("%2", frequencyString).replace("%3", Str.solventStr2Html(spectrum.solventName(this.extendedSolventName)));

		for (i = 0; i < pCount; i++) {
			peak = new Peak(peaks.at(i));
			if (spectrum.checkPeakVisibility(peak) && (!this.compoundOnly || peak.type === Peak.Compound)) { //only the peaks shown in the user interface will be reported
				if (addedPeaksCount !== 0) {
					result += this.pSeparator;
				}
				pString = '';
				pString = pTemplate.arg(peak.delta(1).toFixed(this.deltaPrecision)).arg(peak.delta(2).toFixed(this.deltaPrecision));
				result += pString;
				addedPeaksCount++;
			}
		}
		result = result.replace(/\s*\(\s*\)\s*/g, "");//removes empty ()
		result = result.replace(/,\s*,/g, ",");//removes , ,

		return result;
	},

	report : function (spectrum, createItem) {
		'use strict';

		var peaks = new Peaks(spectrum.peaks()), // get peaks from spectrum,
			peaksF1 = new Peaks(spectrum.peaks()),
			dim = spectrum.dimCount,
			fontStr,
			result = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"><title>Peak Report</title></head><body>",
			i = 1,
			oldLTCount,
			textItem,
			props,
			reportedDims = [],
			italic = "",
			bold = "",
			decoration = "";


		if (this.fontProperties.italic) {
		 	italic = "italic";
		}
		if (this.fontProperties.bold) {
			bold = "bold";
		}
		if (this.fontProperties.underline) {
			decoration = "underline ";
		}
		if (this.fontProperties.strikeOut) {
			decoration += "line-through";
		}

		fontStr= this.start.replace("%1", this.fontProperties.size).
							replace("%2", this.fontProperties.family).
							replace("%3", italic).
							replace("%4", bold).
							replace("%5", decoration).
							replace("%6", this.fontColor);
		result += fontStr;

		if (!peaks.count) { // if no peaks then exit
			return "";
		}
		this.assignedMolecules = undefined;
		if (dim === 1) {
			peaks.sort(this.sortHAscending);
		} else {
			peaks.sort(this.sortHAscending, true);
		}

		if (this.fFlags & PeakReporter.ReportAsPoint) {
			result += this.reportAsPoint(peaks, spectrum);
		} else {
			switch (dim) {
			case 1:
				reportedDims.push(1);
				break;
			default:
				if (this.fFlags & PeakReporter.ReportF1) {
					reportedDims.push(1);
					peaksF1.sort(this.sortHAscending, 1);
				}
				if (this.fFlags & PeakReporter.ReportF2) {
					reportedDims.push(2);
				}
				break;
			}

			for (i = 0; i < reportedDims.length; ++i) {
				if (i) {
					result += "<br/><br/>";
				}
				if (dim === 2 && reportedDims[i] === 1) {
					result += this.reportDimension(reportedDims[i], peaksF1, spectrum);
				} else {
					result += this.reportDimension(reportedDims[i], peaks, spectrum);
				}
			}
		}
		result += this.end + "</body></html>";

		if (createItem) {
			oldLTCount = mainWindow.activeDocument.itemCount("Layout Template");
			props = this.getProperties();
			textItem = draw.text(result, "Report Special", "Peaks," + spectrum.subtype, true, spectrum.uuid, props);
			if (this.fillStyle !== undefined && this.fillStyle === "White") {
				textItem.color = "#ffffff";
				textItem.opacity = 100;
			} else {
				textItem.opacity = 0;
			}
			textItem.update();
			if (oldLTCount === mainWindow.activeDocument.itemCount("Layout Template")) {
				textItem.left = spectrum.left;
				textItem.right = (spectrum.left + spectrum.right) / 2.0;
				textItem.top = spectrum.top;
				textItem.bottom = spectrum.top + (spectrum.bottom - spectrum.top) / 4.0;
			}
			mainWindow.activeDocument.update();
		}
		return result;
	}
}


function reportPeaksUser(createItem, showDialog, props) {
	'use strict';

	if (Env.ReportPeaksUser !== undefined) {
		return Env.ReportPeaksUser(createItem);
	}
	return PeakReporter.reportPeaksUser(createItem, props);
}

function automaticPeakReport() {
	'use strict';

	return reportPeaksUser(false);
}

function customizeReportPeaksUser() {
	'use strict';

	if (Env.ReportPeaksUserSetup !== undefined) {
		return Env.ReportPeaksUserSetup();
	}
	if (Env.DefaultPeakReporter !== undefined) {
		MessageBox.information("Using Default Peak Reporter: " + Env.DefaultPeakReporter.toString(), MessageBox.Ok, MessageBox.NoButton, MessageBox.NoButton, "Information");
	} else {
		var diag = new PeakReportDialog(Env.PeakReportersArray, true);
		diag.exec();
	}
}

function reportPeaks(reporter) {
	'use strict';

	var spectrum = new NMRSpectrum(mainWindow.activeDocument.getActiveItem("NMR Spectrum"));

	reporter.report(spectrum, true);
}
